package com.aispeech.rcprvd.provider.base;

import android.app.Application;
import android.content.Context;
import android.hardware.usb.UsbConstants;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbEndpoint;
import android.hardware.usb.UsbInterface;
import android.hardware.usb.UsbManager;
import android.os.Handler;
import android.os.HandlerThread;
import android.util.Log;

import com.aispeech.rcprvd.utils.UsbCommUtils;
import com.aispeech.rcprvd.utils.UsbPermissionRequestManager;

import androidx.annotation.NonNull;

public abstract class HidDataProvider implements IDataProvider {
    private final String TAG = "HidDataProvider";

    protected HandlerThread mReadThread;
    protected Handler mHandler;

    protected boolean isReading = false;
    protected boolean isPermissionGrant = false;
    protected Application mAppContext;

    protected UsbManager mUsbManager;
    protected UsbDevice mUsbDevice;
    protected UsbInterface mTransferInterface;
    protected UsbDeviceConnection mUsbDeviceConnection;
    protected UsbEndpoint mUsbEndpointIn, mUsbEndpointOut;


    /**
     * permission request runnable
     * no need run here, permission must check before call this lib
     */
    protected Runnable mPermissionCheckRunnable = new Runnable() {
        @Override
        public void run() {
            if (null == mUsbDevice || null == mUsbManager) {
                Log.w(TAG, "mPermissionCheckRunnable: mUsbManager or mUsbDevice is null");
                return;
            }
            if (mUsbManager.hasPermission(mUsbDevice)) {
                Log.d(TAG, "mPermissionCheckRunnable: has permission");
                isPermissionGrant = true;
                tryConnectUsbDevice();
            } else {
                Log.d(TAG, "checkUsbDevicePermission: not permission");
                UsbPermissionRequestManager mUsbPerManager = UsbPermissionRequestManager.getInstance(mAppContext);

                mUsbPerManager.requestPermission(mUsbDevice, new UsbPermissionRequestManager.UsbPmsListener() {
                    @Override
                    public void onSuccess() {
                        Log.d(TAG, "onSuccess: ");
                        isPermissionGrant = true;
                        tryConnectUsbDevice();
                    }

                    @Override
                    public void onFail(String err) {
                        Log.d(TAG, "onFail with: err = " + err + "");
                        isPermissionGrant = false;
                    }
                });
            }
        }
    };

    public HidDataProvider(@NonNull Application mAppContext) {
        this.mAppContext = mAppContext;
        mUsbManager = (UsbManager) mAppContext.getSystemService(Context.USB_SERVICE);

        mReadThread = new HandlerThread("HRT");//hid read thread
        mReadThread.start();
        mHandler = new Handler(mReadThread.getLooper());
    }

    private void tryConnectUsbDevice() {
        Log.d(TAG, "tryConnectUsbDevice: ");
        if (null == mUsbDevice) {
            Log.w(TAG, "tryConnectUsbDevice: can not obtain usb device");
            return;
        }
        if (null == mUsbManager) {
            Log.w(TAG, "tryConnectUsbDevice: can not get usb manager");
        }

        isPermissionGrant = mUsbManager.hasPermission(mUsbDevice);
        Log.d(TAG, "tryConnectUsbDevice: isPermissionGrant = " + isPermissionGrant);
        if (!isPermissionGrant) {
            Log.w(TAG, "tryConnectUsbDevice: usb access permission not grant");
            return;
        }
        mTransferInterface = obtainTransferInterface();
        if (null == mTransferInterface) {
            Log.w(TAG, "tryConnectUsbDevice: can not get usb transfer interface");
            return;
        }

        mUsbDeviceConnection = mUsbManager.openDevice(mUsbDevice);
        boolean claimRet = mUsbDeviceConnection.claimInterface(mTransferInterface, true);
        if (claimRet) {
            Log.d(TAG, "tryConnectUsbDevice: getEndpointCount = " + mTransferInterface.getEndpointCount());
            for (int i = 0; i < mTransferInterface.getEndpointCount(); i++) {
                UsbEndpoint ep = mTransferInterface.getEndpoint(i);
                // out 和in是针对Host来说的
                if (ep.getDirection() == UsbConstants.USB_DIR_OUT) {
                    mUsbEndpointOut = ep;
                } else {
                    mUsbEndpointIn = ep;
                }
            }

            if (null != mUsbEndpointIn) {
                Log.w(TAG, "tryConnectUsbDevice: finish");
            } else {
                Log.e(TAG, "tryConnectUsbDevice: can not get mUsbEndpointIn");
            }
        } else {
            Log.w(TAG, "tryConnectUsbDevice: claimInterface failed");
        }
    }

    protected void tryBreakRequestWait() {
        try {
            Log.d(TAG, "tryBreakRequestWait: ");
            if (null != mUsbManager && null != mUsbDevice && null != mTransferInterface) {
                if (!UsbCommUtils.checkUsbDeviceConnect(mUsbManager, mUsbDevice)) {
                    Log.w(TAG, "tryBreakRequestWait: device may disconnect");
                    return;
                }

                UsbDeviceConnection tmpConn = mUsbManager.openDevice(mUsbDevice);
                Log.d(TAG, "tryBreakRequestWait: openDevice");
                if (null != tmpConn) {
                    tmpConn.claimInterface(mTransferInterface, true);
                    Log.d(TAG, "tryBreakRequestWait: claimInterface");
                    tmpConn.releaseInterface(mTransferInterface);
                    tmpConn.close();
                }
            } else {
                Log.w(TAG, "tryBreakRequestWait: no need try,obj is null !");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public int start() {
        mUsbDevice = obtainUsbDevice();
        tryConnectUsbDevice();
        return 0;
    }

    @Override
    public int stop() {
        return 0;
    }

    protected abstract UsbDevice obtainUsbDevice();

    protected abstract UsbInterface obtainTransferInterface();

}
